<?php
/**
 * Zendex (Zend (Framework) Extensions)
 *
 * LICENSE
 *
 * This source file is subject to the new BSD license that is bundled
 * with this package in the file LICENSE.txt.
 * It is also available through the world-wide-web at this URL:
 * http://zfx.netatoo.fr/license/new-bsd
 * If you did not receive a copy of the license and are unable to
 * obtain it through the world-wide-web, please send an email
 * to support@netatoo.fr so we can send you a copy immediately.
 *
 * @category   Zendex
 * @package    Zendex_Setup
 * @copyright  Copyright (c) 2007 Netatoo FR S.A.R.L. (http://www.netatoo.fr)
 * @license    http://Zendex.netatoo.fr/license/new-bsd     New BSD License
 */

/**
 * @see Zend_Registry
 */
require_once 'Zend/Registry.php';

/**
 * @see Zendex_Setup_Exception
 */
require_once 'Zendex/Setup/Exception.php';

/**
 * @category   Zendex
 * @package    Zendex_Setup
 * @copyright  Copyright (c) 2007 Netatoo FR S.A.R.L. (http://www.netatoo.fr)
 * @license    http://Zendex.netatoo.fr/license/new-bsd     New BSD License
 */
class Zendex_Setup
{
    const DB         = 'Db';
    const CONFIG     = 'Config';
    const CACHE     = 'Cache';
    const LOCALE     = 'Locale';
    const TRANSLATE = 'Translate';
    const AUTH         = 'Auth';
    
    /**
     * Status of self::_init()
     *
     * @var boolean
     */
    private static $_initialized = false;
    
    /**
     * Contain all pre-configuration
     *
     * @var array
     */
    private static $_preConfig = array(
        self::DB            => array(),
        self::CONFIG         => array(),
        self::CACHE            => array(),
        self::LOCALE        => array(),
        self::TRANSLATE        => array(),
        self::AUTH            => array()
    );
    
    /**
     * Pre-configure the configuration object for an instantiation if necessary
     * 
     * @param string $indexName
     * @param string $filename
     * @param string $section
     * @param boolean $allowModifications
     * @param bool/array $cache
     * @return bool
     */
    public static function addConfig($indexName, $type, $filename, $section = null, $allowModifications = false, $cache = false)  
    {
        $indexName = (empty($indexName)) ? 'default' : $indexName ;
        self::$_preConfig['Config'][$indexName] = array();
        self::$_preConfig['Config'][$indexName]['type'] = $type;
        self::$_preConfig['Config'][$indexName]['filename'] = $filename;
        self::$_preConfig['Config'][$indexName]['section']  = $section;
        self::$_preConfig['Config'][$indexName]['allowModifications'] = (boolean) $allowModifications;
        self::$_preConfig['Config'][$indexName]['cache'] = $cache;
        return true;
    }
    
    /**
     * Pre-configure a new database object for an instantiation if necessary.
     * 
     * @param string $indexName
     * @param string/object $adapter
     * @param array $config
     * @param bool/array $cache
     * @return bool
     */
    public static function addDb($indexName, $adapter, $config = array(), $cache = false)  
    {
        $indexName = (empty($indexName)) ? 'default' : $indexName ;
        self::$_preConfig['Db'][$indexName] = array();
        self::$_preConfig['Db'][$indexName]['adapter'] = $adapter;
        self::$_preConfig['Db'][$indexName]['config']  = $config;
        self::$_preConfig['Db'][$indexName]['cache']   = $cache;
        return true;
    }
    
    /**
     * Pre-configure a new cache for an instantiation if necessary.
     * 
     * @param string $indexName
     * @param string $frontend
     * @param string $backend 
     * @param array $frontendOptions 
     * @param array $backendOptions
     * @return bool
     */
    public static function addCache($indexName, $frontend, $backend, $frontendOptions = array(), $backendOptions = array())  
    {
        $indexName = (empty($indexName)) ? 'default' : $indexName ;
        self::$_preConfig['Cache'][$indexName] = array();
        self::$_preConfig['Cache'][$indexName]['frontend']             = $frontend;
        self::$_preConfig['Cache'][$indexName]['backend']              = $backend;
        self::$_preConfig['Cache'][$indexName]['frontendOptions']   = $frontendOptions;
        self::$_preConfig['Cache'][$indexName]['backendOptions']       = $backendOptions;
        return true;
    }
    
    /**
     * Pre-configure the configuration object for an instantiation if necessary
     * 
     * @param string $indexName
     * @param string $filename
     * @param string $section
     * @param boolean $allowModifications
     * @param bool/array $cache
     * @return bool
     */
    public static function addAuth($indexName, $config)  
    {
        $indexName = (empty($indexName)) ? 'default' : $indexName ;
        self::$_preConfig['Auth'][$indexName] = array();
        self::$_preConfig['Auth'][$indexName]['config'] = $config;
        return true;
    }
    
    /**
     * Create a Zend_Config object only if it is not created.
     * 
     * @param string $indexName
     * @return Zend_Config
     */
    public static function getConfig($indexName = 'default') 
    {
        if(!isset(self::$_preConfig['Config'][$indexName])) {
            throw new Zendex_Setup_Exception("The config pre-configuration '$indexName' does not exist in the setup");
        }
        
        $args_id = self::$_preConfig['Config'][$indexName];
        $registryId = "Zend_Config_$indexName";
        
        /**
         * Return the registered object if exist
         */
        if (Zend_Registry::isRegistered($registryId)
        &&  Zend_Registry::get($registryId) instanceof Zend_Config) {
            return Zend_Registry::get($registryId);
        }
        
        /**
         * Otherwise create the Zend_Config object from the parameters defined
         */
        $configClassName = 'Zend_Config_' . ucfirst(strtolower($args_id['type']));
        if(is_string($args_id['cache']) 
        && self::getCache($args_id['cache'])) {
            
            $cache = self::getCache($args_id['cache']);
            
            if(!$cache->test($registryId)) {
                $conf = new $configClassName($args_id['filename'], $args_id['section'], $args_id['allowModifications']);
                $cache->save($conf, $registryId);
            } else {
                $conf = $cache->load($registryId);
            }
        } else {
            $conf = new $configClassName($args_id['filename'], $args_id['section'], $args_id['allowModifications']);
        }
        
        Zend_Registry::set($registryId, $conf); 
        
        return $conf;
    }
      
    /**
     * Create a Zend_Db_Adapter_* object only if it is not created.
     * 
     * @param string $indexName
     * @return Zend_Db_Adapter
     */
    public static function getDb($indexName = 'default') 
    {
        if(!isset(self::$_preConfig['Db'][$indexName])) {
            throw new Zendex_Setup_Exception("The db pre-configuration '$indexName' does not exist in the setup");
        }
        
        $args_id = self::$_preConfig['Db'][$indexName];
        $registryId = "Zend_Db_Adapter_$indexName";

        /**
         * Return the registered object if exist
         */
        if (Zend_Registry::isRegistered($registryId)
        &&  Zend_Registry::get($registryId) instanceof Zend_Db_Adapter_Abstract) {
            $db = Zend_Registry::get($registryId);
            // Connect to database
            $db->getConnection();
            return $db;
        }
    
        /**
         * Otherwise create the Zend_Db_Adapter object from the parameters defined
         */ 
        if(is_string($args_id['cache']) 
        && self::getCache($args_id['cache'])) {
            
            $cache = self::getCache($args_id['cache']);
            
            if(!$cache->test($registryId)) { 
                $db = Zend_Db::factory($args_id['adapter'], $args_id['config']);
                $cache->save($db, $registryId);
            } else {
                $db = $cache->load($registryId);
            }
        } else {
            $db = Zend_Db::factory($args_id['adapter'], $args_id['config']);
        }
        
        $db = Zend_Db::factory($args_id['adapter'], $args_id['config']);
        
        Zend_Registry::set($registryId, $db); 
        
        return $db;
    }
    
    /**
     * Create a Zend_Cache object only if it is not created.
     * 
     * @param string $indexName
     * @return Zend_Cache_Core
     */
    public static function getCache($indexName = 'default') 
    {
        if(!isset(self::$_preConfig['Cache'][$indexName])) {
            throw new Zendex_Setup_Exception("The pre-configuration '$indexName' does not exist in the setup");
        }
        
        $args_id = self::$_preConfig['Cache'][$indexName];
        $registryId = "Zend_Cache_$indexName";
        
        /**
         * Return the registered object if exist
         */
        if (Zend_Registry::isRegistered($registryId)) {
            if(Zend_Registry::get($registryId) instanceof Zend_Cache_Core) {
                return Zend_Registry::get($registryId);
            }
        }
    
        /**
         * Otherwise create the Zend_Cache_Core object from the parameters defined
         * and set in registry
         */
        require_once 'Zend/Cache.php';
        require_once 'Zend/Cache/Core.php';
        $cache = Zend_Cache::factory($args_id['frontend'], $args_id['backend'], $args_id['frontendOptions'], $args_id['backendOptions']);
        Zend_Registry::set($registryId, $cache); 
        
        return $cache;
    }
    
    /**
     * Create a auth object only if it is not created.
     * 
     * @param string $indexName
     * @return Zend_Auth_Adapter_Interface
     */
    public static function getAuth($indexName = 'default') 
    {
        if(!isset(self::$_preConfig['Auth'][$indexName])) {
            throw new Zendex_Setup_Exception("The pre-configuration '$indexName' does not exist in the setup");
        }
        
        $config = self::$_preConfig['Auth'][$indexName];
        
        if($config instanceof Zend_Config) {
            $config = $config->toArray();
        } elseif(is_array($config)) {
            $neededParams =array(
                'storageNamespace', 
                'tableName', 
                'identityColumn',
                'credentialColumn',
                'credentialTreatment'
            );
            
            foreach($neededParams as $index) {
                if(!isset($config[$index])) {
                    new Zendex_Setup_Exception("'$index' parameter is undefined ! (auth:$indexName)");
                }
            }
        } else {
            new Zendex_Setup_Exception("You must define a valid configuration for " 
            . "use an auth adapter ! (auth:$indexName)");
        }
        
        $args_id = self::$_preConfig['Auth'][$indexName];
        $registryId = "Zend_Auth_$indexName";
        
        /**
         * Return the registered object if exist
         */
        if (Zend_Registry::isRegistered($registryId)) {
            if(Zend_Registry::get($registryId) instanceof Zend_Cache_Core) {
                return Zend_Registry::get($registryId);
            }
        }
    
        /**
         * Otherwise create the Zend_Cache_Core object from the parameters defined
         * and set in registry
         */
        $cache = Zend_Cache::factory(
            $args_id['frontend'], 
            $args_id['backend'], 
            $args_id['frontendOptions'], 
            $args_id['backendOptions']
        );
        Zend_Registry::set($registryId, $cache); 
        
        return $cache;
    }
    
    /**
     * Sets one or more local available for the implementation 
     * (correlated with translation files)
     * 
     * @param array/Zend_Config $config
     * @param boolean    $withoutNow If true, get_included_path() is included
     * @trown Zendex_Setup_Exception if invalid configuration
     * @return void
     */
    public static function setIncludePath($paths, $separator, $createConstants = false, $withoutCurrent = false)  
    {    
        if(!in_array($separator, array(':', ';'))) {
            throw new Zendex_Setup_Exception("Invalid configuration : you must define 'separator' index with ';' or ':'");
        }
         
        if(!is_array($paths) && !$paths instanceof SimpleXMLElement) {
            throw new Zendex_Setup_Exception("Invalid configuration : you must define 'paths' on array assoc.");
        }
        
        $include_path = '.' . $separator;
        
        foreach($paths as $name => $path) {
            $include_path.= $path . $separator;
            if(true === (bool) $createConstants) {
                $name = strtoupper($name);
                if(!defined($name)) {
                    define($name, $path);
                }
            }
        }
        
        if(!$withoutCurrent) {
            $include_path.= get_include_path();
        }
        
        set_include_path($include_path); 
    }
    
    /**
     * Create a Zend_Db_Adapter_* object only if it is not created.
     * 
     * @param string $indexName
     * @return Zend_Db_Adapter
     */
    public static function getTranslate() //$indexName = 'default'
    {
        // is temporary
        return Zend_Registry::get('lang');
    }
      
    /**
     * Sets one or more local available for the implementation 
     * (correlated with translation files)
     * 
     * @params array/string $locale
     * @return bool
     */
    public static function setLocale($locale = null)  
    {
        if(is_string($locale)) {
            self::$_preConfig['Locale'][$locale] = new Zend_Locale($locale);
            return true;
        }
         
        if(is_array($locale)) {
            foreach ($locale as $localeName) {
                self::$_preConfig['Locale'][$localeName] = new Zend_Locale($localeName);
            }
            return true;
        }
        
        return false;
    }

    /**
     * This method makes it possible to recover a local registered beforehand.
     * 
     * There are two features:
     * - $locale is 'auto', in this case, gets the local' self 'Zend_Locale(), 
     * if that is not available locally in our pre-defined list, 
     * it returns the default locale (the first in the list)
     * - $locale is like 'us' => won the local defined if
     * 
     * @params array/string $locale
     * @return Zend_Locale
     */
    public static function getLocale($locale = 'auto')  
    {
        if(isset(self::$_preConfig['Locale'][$locale])) {
            return self::$_preConfig['Locale'][$locale];;
        } else {
            $autoLocale = new Zend_Locale();
            $autoLanguage = $autoLocale->getLanguage();
            
            if(array_key_exists($autoLanguage, self::$_preConfig['Locale'])) {
                return $autoLocale;
            } else {
                echo 'firstLocale';
                foreach (self::$_preConfig['Locale'] as $locale) {
                    return $locale;
                }
                
            }
        }
        
        return false;
    }
        
    /**
     * Get all locale available for the implementation 
     * (correlated with translation files)
     * 
     * @params array/string $locale
     * @return bool
     */
    public static function getLocaleList()  
    {
    }
    
    /**
     * Get all locale available for the implementation 
     * (correlated with translation files)
     * 
     * @param string $registryIndex 
     * @param bool $sessionTimeUpdate
     * @return Zend_Auth_Adapter_Interface
     */
    public static function authAccess() 
    {
        $auth = Zend_Auth::getInstance();
        $auth->setStorage(new Zend_Auth_Storage_Session('kelcourt_desktop_auth'));
        
        if ($auth->hasIdentity()) {
            return $auth->getIdentity();
        }
        return false;
    }
    
    /**
     * Clear an item
     * 
     * @param string $indexName
     * @return Zend_Db_Adapter
     */
    public static function clearItem($type, $index)
    {
        if(!array_key_exists($type, self::$_preConfig)) {
            return false;
        }
        
        if(!isset(self::$_preConfig[$type][$index])) {
            return false;
        }
        
        unset(self::$_preConfig[$type][$index]);
        return true;
    }
    
}
